﻿using System;
using System.Configuration;
using Castle.Core.Configuration;
using Castle.MicroKernel;
using Mbs.Reflection;

namespace Mbs.CastleContainer
{
    class ComponentInstaller
    {
        public ComponentInstaller()
        {
            
        }

        public void SetUp(IKernel container, IConfigurationStore store)
        {
            var classLoader = ServiceLocator.Current.Get<IClassLoader>();
            SetUpFacilities(store.GetFacilities(), container, classLoader);
            SetUpComponents(store.GetComponents(), container, classLoader);
        }

        public void SetUpFacilities(IConfiguration[] configurations, IKernel container, IClassLoader classLoader)
        {
            foreach (IConfiguration facility in configurations)
            {
                String id = facility.Attributes["id"];
                String typeName = facility.Attributes["type"];
                if (typeName == null || typeName.Length == 0) continue;

                Type type = ObtainType(typeName, classLoader);
                if (string.IsNullOrEmpty(id))
                    id = type.FullName;

                IFacility facilityInstance = InstantiateFacility(type);


                System.Diagnostics.Debug.Assert(facilityInstance != null);

                container.AddFacility(id, facilityInstance);
            }
        }

        public void SetUpComponents(IConfiguration[] configurations, IKernel container, IClassLoader classLoader)
        {
            foreach (IConfiguration component in configurations)
            {
                String id = component.Attributes["id"];

                String typeName = component.Attributes["type"];
                String serviceTypeName = component.Attributes["service"];

                if (typeName == null || typeName.Length == 0) continue;

                Type type = ObtainType(typeName, classLoader);
                Type service = type;

                if (serviceTypeName != null && serviceTypeName.Length != 0)
                {
                    service = ObtainType(serviceTypeName, classLoader);
                }

                System.Diagnostics.Debug.Assert(type != null);
                System.Diagnostics.Debug.Assert(service != null);

                //var registration = Component.For(service).ImplementedBy(type);
                //if (!string.IsNullOrEmpty(id))
                //    registration.Named(id);

                //container.Do(registration);
                if (!container.HasComponent(id))
                    container.AddComponent(id, service, type);
            }
        }

        private Type ObtainType(String typeName, IClassLoader classLoader)
        {
            typeName = typeName.Trim();
            Type type = Type.GetType(typeName, false, false);

            if (type == null)
            {
                
                type = classLoader.LoadType(typeName); 
                if (type != null)
                    return type;

                ThrowIfTypeNameNotExists(typeName);
            }

            return type;
        }

        private static void ThrowIfTypeNameNotExists(String typeName)
        {
            String message = String.Format("The type name {0} could not be located", typeName);
            throw new ConfigurationErrorsException(message);
        }

        private IFacility InstantiateFacility(Type facilityType)
        {
            if (!typeof(IFacility).IsAssignableFrom(facilityType))
            {
                String message = String.Format("Type {0} does not implement the interface IFacility", facilityType.FullName);
                throw new ConfigurationErrorsException(message);
            }

            try
            {
                return (IFacility)Activator.CreateInstance(facilityType);
            }
            catch (Exception)
            {
                String message = String.Format("Could not instantiate {0}. Does it have a public default constructor?", facilityType.FullName);
                throw new ConfigurationErrorsException(message);
            }
        }
    }
}
